package main

import (
	"errors"
	"fmt"
)

// 自定义的 error 类型
type PathError struct {
	Op   string
	Path string
	Err  error
}

func (e *PathError) Error() string {
	return e.Op + " " + e.Path + ": " + e.Err.Error()
}

func main() {
	n, err := Add(-1, -1)
	if err != nil {
		// 错误处理
		fmt.Println("error")
	} else {
		// 使用返回值n
		fmt.Println(n)
	}

	// 自定义error的使用
	//	&PathError{"stat", name, err} // 使用error
	// 在 if 之后，条件语句之前，可以添加变量初始化语句，使用 ; 间隔；
	//	if e, ok := err.(*os.PathError); ok && e.Err != nil {
	//		// 获取PathError类型变量e中的其他信息并处理
	//	}

	// 2.6.2defer
	// 直接执行， defer 语句的调用是遵照先进后出的原则，即最后一个 defer 语句将最先被执行
	//	defer func() {
	//		// 做你复杂的清理工作
	//	}()

	// 2.6.3  panic() 和 recover()
	// Go语言引入了两个内置函数 panic() 和 recover() 以报告和处理运行时错误和程序中的错误场景
	//    func panic(interface{})
	//    func recover() interface{}

	//当在一个函数执行过程中调用 panic() 函数时，正常的函数执行流程将立即终止，但函数中
	//之前使用 defer 关键字延迟执行的语句将正常展开执行，之后该函数将返回到调用函数，并导致
	//逐层向上执行 panic 流程，直至所属的goroutine中所有正在执行的函数被终止。错误信息将被报
	//告，包括在调用 panic() 函数时传入的参数，这个过程称为错误处理流程。
	//panic(404)
	//panic("network broken")
	//panic(Error("file not exists"))

	//recover() 函数用于终止错误处理流程。一般情况下， recover() 应该在一个使用 defer
	//关键字的函数中执行以有效截取错误处理流程。如果没有在发生异常的goroutine中明确调用恢复
	//过程（使用 recover 关键字），会导致该goroutine所属的进程打印异常信息后直接退出。
	//这个过程称为错误处理流程。

	//以下为一个常见的场景。
	//我们对于 foo() 函数的执行要么心里没底感觉可能会触发错误处理，或者自己在其中明确加
	//入了按特定条件触发错误处理的语句，那么可以用如下方式在调用代码中截取 recover() ：
	//	defer func() {
	//		if r := recover(); r != nil {
	//			log.Printf("Runtime error caught: %v", r)
	//		}
	//	}()
	//	foo()

	/*无论 foo() 中是否触发了错误处理流程，该匿名 defer 函数都将在函数退出时得到执行。假
	  如 foo() 中触发了错误处理流程， recover() 函数执行将使得该错误处理过程终止。如果错误处
	  理流程被触发时，程序传给 panic 函数的参数不为 nil ，则该函数还会打印详细的错误信息。*/

}

// defer释放资源
//func CopyFile(dst, src string) (w int64, err error) {
//	srcFile, err := os.Open(src)
//	if err != nil {
//		return
//	}
//	defer srcFile.Close()
//	dstFile, err := os.Create(dstName)
//	if err != nil {
//		return
//	}
//	defer dstFile.Close()
//	return io.Copy(dstFile, srcFile)
//}

// 将 error 作为多种返回值中的最后一个，但这并非是强制要求
func Add(a int, b int) (ret int, err error) {
	if a < 0 || b < 0 { // 假设这个函数只支持两个非负数字的加法
		err = errors.New("Should be non-negative numbers!")
		return 0, err
	}
	return a + b, nil // 支持多重返回值
}
